Managing Game DataUsing SQLite DatabaseOn this pageUsing SQLite Database Dora SSR engine provides developers with integrated SQLite database support, which can be used to manage and query large amounts of game data, as well as store critical game data persistently. This tutorial will guide you from scratch, step by step, through the process of using Dora's database features. 1. Introduction In game development, you often need to manage large amounts of game data such as player information, game items, and level configurations. Using a database allows you to store and retrieve this data efficiently. The Dora SSR engine integrates the SQLite database and provides a simple and easy-to-use interface for database operations. 1.1 Getting Started with Database Operations Before we begin, make sure you understand the following concepts: Database: A system that stores and manages data. Table: The structure in a database that stores data, consisting of rows and columns. Row: A record in a table. Column: A collection of a specific attribute in the table. 2. Introduction to the DB Class The DB class is the core class provided by Dora for database operations. With this class, you can perform the following actions: Check if a table exists Execute SQL queries Insert, update, and delete data Execute transactions Perform asynchronous operations 2.1 Important Concepts Related to the DB Class Column: Represents a database column type, which can be integer, number, string, or boolean. Special NoteIn Dora SSR, the boolean type in the database column only supports the false value to represent a NULL value in the database. If you need to store boolean values in the database, use the numeric types 0 and 1 to represent false and true. Row: Represents a row of data in the database, usually a Lua table containing multiple Columns. SQL: Represents an SQL query, which can be a string or a string with parameters in combination with a parameter table. 3. Basic Operation Examples Let's get familiar with the usage of the DB class through some basic operations. 3.1 Checking if a Table Exists Before working with the database, you typically need to confirm whether a table exists. You can use the exist method: LuaTealTypeScriptYueScriptlocal tableExists = DB:exist("test_table")print(tableExists and "Table exists" or "Table does not exist")local tableExists = DB:exist("test_table")print(tableExists and "Table exists" or "Table does not exist")const tableExists = DB.exist("test_table");print(tableExists ? "Table exists" : "Table does not exist");tableExists = DB\exist "test_table"print tableExists and "Table exists" or "Table does not exist" 3.2 Creating and Dropping a Table You can use the exec method to execute SQL statements to create and drop tables: LuaTealTypeScriptYueScript-- Drop the table named test_table if it existsDB:exec("DROP TABLE IF EXISTS test_table")-- Create the table named test_tableDB:exec("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")-- Drop the table named test_table if it existsDB.exec("DROP TABLE IF EXISTS test_table")-- Create the table named test_tableDB:exec("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)")// Drop the table named test_table if it existsDB.exec("DROP TABLE IF EXISTS test_table");// Create the table named test_tableDB.exec("CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)");-- Drop the table named test_table if it existsDB\exec "DROP TABLE IF EXISTS test_table"-- Create the table named test_tableDB\exec "CREATE TABLE test_table (id INTEGER PRIMARY KEY, value TEXT)" 3.3 Inserting Data You can insert data into a table using the insert method: LuaTealTypeScriptYueScriptlocal success = DB:insert("test_table", { {1, "Hello"}, {2, "World"}})print(success and "Insert successful" or "Insert failed")local success = DB:insert("test_table", { {1, "Hello"}, {2, "World"}})print(success and "Insert successful" or "Insert failed")const success = DB.insert("test_table", [ [1, "Hello"], [2, "World"]]);print(success ? "Insert successful" : "Insert failed");success = DB\insert "test_table", [ [1, "Hello"], [2, "World"]]print success and "Insert successful" or "Insert failed" 3.4 Querying Data You can query data from the table using the query method: LuaTealTypeScriptYueScriptlocal results = DB:query("SELECT * FROM test_table")for _, row in ipairs(results) do print("ID:", row[1], "Value:", row[2])endlocal results = DB:query("SELECT * FROM test_table")if not results is nil then for _, row in ipairs(results) do print("ID:", row[1], "Value:", row[2]) endendconst results = DB.query("SELECT * FROM test_table");if (results) { for (const [id, value] of results) { print("ID:", id, "Value:", value); }}results = DB\query "SELECT * FROM test_table"for [id, value] in *results print "ID:", id, "Value:", value 3.5 Updating and Deleting Data You can perform updates and deletions using the exec method: LuaTealTypeScriptYueScript-- Update datalocal rowsAffected = DB:exec("UPDATE test_table SET value = ? WHERE id = ?", {"Hello Dora", 1})print("Rows updated:", rowsAffected)-- Delete datarowsAffected = DB:exec("DELETE FROM test_table WHERE id = ?", {2})print("Rows deleted:", rowsAffected)-- Update datalocal rowsAffected = DB:exec("UPDATE test_table SET value = ? WHERE id = ?", {"Hello Dora", 1})print("Rows updated:", rowsAffected)-- Delete datarowsAffected = DB:exec("DELETE FROM test_table WHERE id = ?", {2})print("Rows deleted:", rowsAffected)// Update datalet rowsAffected = DB.exec("UPDATE test_table SET value = ? WHERE id = ?", ["Hello Dora", 1]);print("Rows updated:", rowsAffected);// Delete datarowsAffected = DB.exec("DELETE FROM test_table WHERE id = ?", [2]);print("Rows deleted:", rowsAffected);-- Update datarowsAffected = DB\exec "UPDATE test_table SET value = ? WHERE id = ?", ["Hello Dora", 1]print "Rows updated:", rowsAffected-- Delete datarowsAffected = DB\exec "DELETE FROM test_table WHERE id = ?", [2]print "Rows deleted:", rowsAffected 4. Transactions A transaction is a set of operations that either all succeed or, if an error occurs, none are executed. In Dora SSR, you can use the transaction method to execute transactions: LuaTealTypeScriptYueScriptlocal sqlStatements = { "INSERT INTO test_table (id, value) VALUES (3, 'Dora')", "INSERT INTO test_table (id, value) VALUES (4, 'SSR')"}local transactionSuccess = DB:transaction(sqlStatements)print(transactionSuccess and "Transaction successful" or "Transaction failed")local sqlStatements = { "INSERT INTO test_table (id, value) VALUES (3, 'Dora')", "INSERT INTO test_table (id, value) VALUES (4, 'SSR')"}local transactionSuccess = DB:transaction(sqlStatements)print("Transaction successful")const sqlStatements = [ "INSERT INTO test_table (id, value) VALUES (3, 'Dora')", "INSERT INTO test_table (id, value) VALUES (4, 'SSR')"];const transactionSuccess = DB.transaction(sqlStatements);print(transactionSuccess ? "Transaction successful" : "Transaction failed");sqlStatements = [ "INSERT INTO test_table (id, value) VALUES (3, 'Dora')", "INSERT INTO test_table (id, value) VALUES (4, 'SSR')"]transactionSuccess = DB\transaction sqlStatementsprint transactionSuccess and "Transaction successful" or "Transaction failed" 5. Asynchronous Operations To avoid blocking the main thread, Dora SSR provides asynchronous methods such as insertAsync, queryAsync, and execAsync to perform database operations in a background thread. LuaTealTypeScriptYueScriptthread(function() -- Asynchronous insert data DB:insertAsync("test_table", { {5, "Async"}, {6, "Operation"} }) -- Asynchronous query data local asyncResults = DB:queryAsync("SELECT * FROM test_table") if asyncResults then for _, row in ipairs(asyncResults) do print("Async Query - ID:", row[1], "Value:", row[2]) end endend)thread(function() -- Asynchronous insert data DB:insertAsync("test_table", { {5, "Async"}, {6, "Operation"} }) -- Asynchronous query data local asyncResults = DB:queryAsync("SELECT * FROM test_table") if not asyncResults is nil then for _, row in ipairs(asyncResults) do print("Async Query - ID:", row[1], "Value:", row[2]) end endend)thread(() => { // Asynchronous insert data DB.insertAsync("test_table", [ [5, "Async"], [6, "Operation"] ]); // Asynchronous query data const asyncResults = DB.queryAsync("SELECT * FROM test_table"); if (asyncResults) { for (const [id, value] of asyncResults) { print("Async Query - ID:", id, "Value:", value); } }});thread -> -- Asynchronous insert data DB\insertAsync "test_table", [ [5, "Async"], [6, "Operation"] ] -- Asynchronous query data asyncResults = DB\queryAsync "SELECT * FROM test_table" for [id, value] in *asyncResults print "Async Query - ID:", id, "Value:", value 6. Creating a New Schema In real-world projects, you may need to create a new schema to store and manage game data separately instead of storing everything in the same default database. A schema is a logical structure in the database, like a grouping of multiple tables. You can use the exec method to create schemas. LuaTealTypeScriptYueScript-- Define the full path of the file to store the new schema data, it must be in the engine's writable directorylocal schemaFile = Path(Content.writablePath, "game_data.db")-- Create schema,-- the database file will be automatically created if it doesn't exist,-- and will be added to the current database connection if it does existDB:exec("ATTACH DATABASE '" .. schemaFile .. "' AS game_data")-- Create tables in the new schema and insert dataDB:exec("CREATE TABLE game_data.player (id INTEGER PRIMARY KEY, name TEXT)")DB:insert("game_data.player", {false, "Dora"})-- Query the data from the new schemaDB:query("SELECT * FROM game_data.player")-- Detach schema so that the data from the new schema is no longer accessibleDB:exec("DETACH DATABASE game_data")-- Define the full path of the file to store the new schema data, it must be in the engine's writable directorylocal schemaFile = Path(Content.writablePath, "game_data.db")-- Create schema,-- the database file will be automatically created if it doesn't exist,-- and will be added to the current database connection if it does existDB:exec("ATTACH DATABASE '" .. schemaFile .. "' AS game_data")-- Create tables in the new schema and insert dataDB:exec("CREATE TABLE game_data.player (id INTEGER PRIMARY KEY, name TEXT)")DB:insert("game_data.player", {false, "Dora"})-- Query the data from the new schemaDB:query("SELECT * FROM game_data.player")-- Detach schema so that the data from the new schema is no longer accessibleDB:exec("DETACH DATABASE game_data")// Define the full path of the file to store the new schema data, it must be in the engine's writable directoryconst schemaFile = Path(Content.writablePath, "game_data.db");// Create schema,// the database file will be automatically created if it doesn't exist,// and will be added to the current database connection if it does existDB.exec(`ATTACH DATABASE '${schemaFile}' AS game_data`);// Create tables in the new schema and insert dataDB.exec("CREATE TABLE game_data.player (id INTEGER PRIMARY KEY, name TEXT)");DB.insert("game_data.player", [false, "Dora"]);// Query the data from the new schemaDB.query("SELECT * FROM game_data.player");// Detach schema so that the data from the new schema is no longer accessibleDB.exec("DETACH DATABASE game_data");-- Define the full path of the file to store the new schema data, it must be in the engine's writable directoryschemaFile = Path(Content\writablePath, "game_data.db")-- Create schema,-- the database file will be automatically created if it doesn't exist,-- and will be added to the current database connection if it does existDB\exec "ATTACH DATABASE '#{schemaFile}' AS game_data"-- Create tables in the new schema and insert dataDB\exec "CREATE TABLE game_data.player (id INTEGER PRIMARY KEY, name TEXT)"DB\insert "game_data.player", [false, "Dora"]-- Query the data from the new schemaDB\query "SELECT * FROM game_data.player"-- Detach schema so that the data from the new schema is no longer accessibleDB\exec "DETACH DATABASE game_data" SummaryThe default schema library provided by the Dora SSR engine will be stored in the file corresponding to the path Path(Content.writablePath, "dora.db"). When accessing tables, if you do not prefix the table name, it means accessing tables in the default schema. If you need to create a new schema and store it in a separate database file for easier migration, you can use ATTACH DATABASE and DETACH DATABASE for this operation. 7. Importing Excel Data into the Database In game development, Excel spreadsheets are often used to manage game data. When the data volume in Excel becomes large and complex queries such as data associations are needed, using a database becomes more efficient. Dora SSR engine provides convenient functionality to import Excel data into the database, making it easier for developers to manage game data. 7.1 Prerequisites Consistent Table Structure and Excel Sheet Structure: Ensure that the table structure ( column names and column types) in the database matches the data columns in the Excel worksheet. Excel File Format: Currently supported Excel file format is .xlsx. 7.2 Example Steps Let's walk through an example of how to import Excel data into the database. Create Database Table Suppose we have an Excel file data.xlsx containing a worksheet Items, which records game item information, including item ID, name, and description. First, we need to create the corresponding table in the database: LuaTealTypeScriptYueScriptDB:exec([[ CREATE TABLE IF NOT EXISTS Items ( id INTEGER PRIMARY KEY, name TEXT, description TEXT )]])DB:exec([[ CREATE TABLE IF NOT EXISTS Items ( id INTEGER PRIMARY KEY, name TEXT, description TEXT )]])DB.exec(` CREATE TABLE IF NOT EXISTS Items ( id INTEGER PRIMARY KEY, name TEXT, description TEXT )`);DB\exec [[ CREATE TABLE IF NOT EXISTS Items ( id INTEGER PRIMARY KEY, name TEXT, description TEXT )]] Prepare Excel File Ensure that your Excel file data.xlsx is located in an accessible path in the project, and that the first row of the worksheet Items contains the column names, corresponding to the column names in the database table: idnamedescription1SwordBasic sword2ShieldBasic shield......... Use DB.insertAsync to Import Data LuaTealTypeScriptYueScriptthread(function() local success = DB:insertAsync( {"Items"}, "data.xlsx", 2 ) if success then print("Excel data imported successfully!") else print("Excel data import failed!") endend)thread(function() local success = DB:insertAsync( {"Items"}, "data.xlsx", 2 ) if success then print("Excel data imported successfully!") else print("Excel data import failed!") endend)thread(() => { const success = DB.insertAsync( ["Items"], "data.xlsx", 2 ); if (success) { print("Excel data imported successfully!"); } else { print("Excel data import failed!"); }});thread -> success = DB\insertAsync( ["Items",] "data.xlsx" 2 ) if success print "Excel data imported successfully!" else print "Excel data import failed!" Verify Import Results You can query the database to verify that the data was imported successfully: LuaTealTypeScriptYueScriptlocal items = DB:query("SELECT * FROM Items")for _, item in ipairs(items) do print("ID:", item[1], "Name:", item[2], "Description:", item[3])endlocal items = DB:query("SELECT * FROM Items")if not items is nil then for _, item in ipairs(items) do print("ID:", item[1], "Name:", item[2], "Description:", item[3]) endendconst items = DB.query("SELECT * FROM Items");if (items) { for (const [id, name, description] of items) { print("ID:", id, "Name:", name, "Description:", description); }}items = DB\query "SELECT * FROM Items"for [id, name, description] in *items print "ID:", id, "Name:", name, "Description:", description 7.3 Using a Custom Worksheet Name If the worksheet name in your Excel file differs from the table name in the database, you can specify the corresponding relationship: LuaTealTypeScriptYueScriptthread(function() local tableSheets = { {"Items", "GameItems"} -- The database table name is "Items", corresponding to the Excel worksheet name "GameItems" } local success = DB:insertAsync( tableSheets, "data.xlsx", 2 ) if success then print("Excel data imported successfully!") else print("Excel data import failed!") endend)thread(function() local tableSheets = { {"Items", "GameItems"} -- The database table name is "Items", corresponding to the Excel worksheet name "GameItems" } local success = DB:insertAsync( tableSheets, "data.xlsx", 2 ) if success then print("Excel data imported successfully!") else print("Excel data import failed!") endend)thread(() => { const tableSheets = [ ["Items", "GameItems"] // The database table name is "Items", corresponding to the Excel worksheet name "GameItems" ]; const success = DB.insertAsync( tableSheets, "data.xlsx", 2 ); if (success) { print("Excel data imported successfully!"); } else { print("Excel data import failed!"); }});thread -> tableSheets = [ ["Items", "GameItems"] -- The database table name is "Items", corresponding to the Excel worksheet name "GameItems" ] success = DB\insertAsync( tableSheets, "data.xlsx", 2 ) if success print "Excel data imported successfully!" else print "Excel data import failed!" 7.4 Notes Data Type Matching: Ensure that the data types in Excel are compatible with the column types in the database table, for example, numeric data should correspond to INTEGER or REAL types, and text data should correspond to TEXT types. Handling Dates and Boolean Values: Dates and boolean values in Excel need to be appropriately converted before importing to match the database column types. Error Handling: The insertAsync method will return false if an error occurs during the import process. It is recommended to add error logs in actual applications to capture and handle potential exceptions. 8. Full Sample Code Below is a complete sample code, which we will break down line by line. LuaTealTypeScriptYueScriptlocal DB <const> = require("DB")local thread <const> = require("thread")-- Use a transaction to create a table and insert initial datalocal sqls = { "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", { "INSERT INTO test VALUES(?, ?)", { {false, "hello"}, {false, "world"}, {false, "ok"} } }}local result = DB:transaction(sqls)print(result and "Success" or "Failure")-- Check if the table existsprint(DB:exist("test"))-- Query and print datap(DB:query("SELECT * FROM test", true))-- Delete and update dataprint("row changed:", DB:exec("DELETE FROM test WHERE id > 1"))print("row changed:", DB:exec("UPDATE test SET value = ? WHERE id = 1", {"hello world!"}))-- Perform asynchronous operationsthread(function() -- Asynchronous insert data print("insert async") local data = {} local count = 1 for k in pairs(_G) do data[count] = {false, k} count = count + 1 end p(DB:insertAsync("test", data)) -- Asynchronous query data print("query async...") local items = DB:queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC") local rows = {} if items then count = 1 for i = 1, #items do local item = items[i] rows[count] = item[1] count = count + 1 end end p(rows)end)local DB <const> = require("DB")local thread <const> = require("thread")-- Use a transaction to create a table and insert initial datalocal sqls = { "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", { "INSERT INTO test VALUES(?, ?)", { {false, "hello"}, {false, "world"}, {false, "ok"} } }}local result = DB:transaction(sqls)print(result and "Success" or "Failure")-- Check if the table existsprint(DB:exist("test"))-- Query and print datap(DB:query("SELECT * FROM test", true))-- Delete and update dataprint("row changed:", DB:exec("DELETE FROM test WHERE id > 1"))print("row changed:", DB:exec("UPDATE test SET value = ? WHERE id = 1", {"hello world!"}))-- Perform asynchronous operationsthread(function() -- Asynchronous insert data print("insert async") local data = {} local count = 1 for k in pairs(_G as {string: any}) do data[count] = {false, k} count = count + 1 end p(DB:insertAsync("test", data)) -- Asynchronous query data print("query async...") local items = DB:queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC") local rows = {} if not items is nil then count = 1 for i = 1, #items do local item = items[i] rows[count] = item[1] count = count + 1 end end p(rows)end)import { DB, thread, SQL } from "Dora";// Use a transaction to create a table and insert initial dataconst sqls: SQL[] = [ "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", [ "INSERT INTO test VALUES(?, ?)", [ [false, "hello"], [false, "world"], [false, "ok"] ] ]];const result = DB.transaction(sqls);print(result ? "Success" : "Failure");// Check if the table existsprint(DB.exist("test"));// Query and print datap(DB.query("SELECT * FROM test", true));// Delete and update dataprint("row changed:", DB.exec("DELETE FROM test WHERE id > 1"));print("row changed:", DB.exec("UPDATE test SET value = ? WHERE id = 1", ["hello world!"]));// Perform asynchronous operationsthread(() => { // Asynchronous insert data print("insert async"); const data = []; let count = 1; for (const k in _G) { data[count] = [false, k]; count++; } p(DB.insertAsync("test", data)); // Asynchronous query data print("query async..."); const items = DB.queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC"); const rows = []; if (items) { for (let i = 0; i < items.length; i++) { const item = items[i]; rows.push(item[0]); } } p(rows);});_ENV = Dora-- Use a transaction to create a table and insert initial datasqls = [ "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", [ "INSERT INTO test VALUES(?, ?)", [ [false, "hello"], [false, "world"], [false, "ok"] ] ]]result = DB\transaction sqlsprint result and "Success" or "Failure"-- Check if the table existsprint DB\exist "test"-- Query and print datap DB\query "SELECT * FROM test", true-- Delete and update dataprint "row changed:", DB\exec "DELETE FROM test WHERE id > 1"print "row changed:", DB\exec "UPDATE test SET value = ? WHERE id = 1", ["hello world!",]-- Perform asynchronous operationsthread -> -- Asynchronous insert data print "insert async" data = [] for k in pairs _G data[] = [false, k] p DB\insertAsync "test", data -- Asynchronous query data print "query async..." items = DB\queryAsync "SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC" rows = [] for i = 1, #items item = items[i] rows[] = item[1] p rows 8.1 Code Breakdown Import Modules: LuaTealTypeScriptYueScriptlocal DB <const> = require("DB")local thread <const> = require("thread")local DB <const> = require("DB")local thread <const> = require("thread")import { DB, thread, SQL } from "Dora";_ENV = Dora The DB module is for database operations. The thread module is used for creating asynchronous threads. Define SQL Statement List: LuaTealTypeScriptYueScriptlocal sqls = { "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", { "INSERT INTO test VALUES(?, ?)", { {false, "hello"}, -- Using false as a placeholder for NULL value in the database, id will auto-increment {false, "world"}, {false, "ok"} } }}local sqls = { "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", { "INSERT INTO test VALUES(?, ?)", { {false, "hello"}, -- Using false as a placeholder for NULL value in the database, id will auto-increment {false, "world"}, {false, "ok"} } }}const sqls: SQL[] = [ "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", [ "INSERT INTO test VALUES(?, ?)", [ [false, "hello"], // Using false as a placeholder for NULL value in the database, id will auto-increment [false, "world"], [false, "ok"] ] ]];sqls = [ "DROP TABLE IF EXISTS test", "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", [ "INSERT INTO test VALUES(?, ?)", [ [false, "hello"], -- Using false as a placeholder for NULL value in the database, id will auto-increment [false, "world"], [false, "ok"] ] ]] Drop the table named test if it exists. Create a table named test with two columns: id and value. Insert three rows of data. The id column will auto-increment (using false as a placeholder for NULL), and the value column will have "hello", "world", and "ok" values. Execute Transaction: LuaTealTypeScriptYueScriptlocal result = DB:transaction(sqls)print(result and "Success" or "Failure")local result = DB:transaction(sqls)print(result and "Success" or "Failure")const result = DB.transaction(sqls);print(result ? "Success" : "Failure");result = DB\transaction sqlsprint result and "Success" or "Failure" Use the transaction method to execute the SQL statements as a transaction, ensuring atomicity. Print the result of the transaction. Check if Table Exists: LuaTealTypeScriptYueScriptprint(DB:exist("test"))print(DB:exist("test"))print(DB.exist("test"));print DB\exist "test" Check if the test table exists. Query and Print Data: LuaTealTypeScriptYueScriptp(DB:query("SELECT * FROM test", true))p(DB:query("SELECT * FROM test", true))p(DB.query("SELECT * FROM test", true));p DB\query "SELECT * FROM test", true Query all data from the test table, true indicates that the result includes the column names. Use the p function (a special engine-provided print function) to print the query result. Delete and Update Data: LuaTealTypeScriptYueScriptprint("row changed:", DB:exec("DELETE FROM test WHERE id > 1"))print("row changed:", DB:exec("UPDATE test SET value = ? WHERE id = 1", {"hello world!"}))print("row changed:", DB:exec("DELETE FROM test WHERE id > 1"))print("row changed:", DB:exec("UPDATE test SET value = ? WHERE id = 1", {"hello world!"}))print("row changed:", DB.exec("DELETE FROM test WHERE id > 1"));print("row changed:", DB.exec("UPDATE test SET value = ? WHERE id = 1", ["hello world!"]));print "row changed:", DB\exec "DELETE FROM test WHERE id > 1"print "row changed:", DB\exec "UPDATE test SET value = ? WHERE id = 1", ["hello world!",] Delete rows where id is greater than 1. Update the row where id is equal to 1, changing the value to "hello world!". Print the number of affected rows. Asynchronous Operations: LuaTealTypeScriptYueScriptthread(function() -- Asynchronous insert data print("insert async") local data = {} local count = 1 for k in pairs(_G) do data[count] = {false, k} count = count + 1 end p(DB:insertAsync("test", data)) -- Asynchronous query data print("query async...") local items = DB:queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC") local rows = {} if items then count = 1 for i = 1, #items do local item = items[i] rows[count] = item[1] count = count + 1 end end p(rows)end)thread(function() -- Asynchronous insert data print("insert async") local data = {} local count = 1 for k in pairs(_G as {string: any}) do data[count] = {false, k} count = count + 1 end p(DB:insertAsync("test", data)) -- Asynchronous query data print("query async...") local items = DB:queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC") local rows = {} if not items is nil then count = 1 for i = 1, #items do local item = items[i] rows[count] = item[1] count = count + 1 end end p(rows)end)thread(() => { // Asynchronous insert data print("insert async"); const data = []; let count = 1; for (const k in _G) { data[count] = [false, k]; count++; } p(DB.insertAsync("test", data)); // Asynchronous query data print("query async..."); const items = DB.queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC"); const rows = []; if (items) { for (let i = 0; i < items.length; i++) { const item = items[i]; rows.push(item[0]); } } p(rows);});thread -> -- Asynchronous insert data print "insert async" data = [] for k in pairs _G data[] = [false, k] p DB\insertAsync "test", data -- Asynchronous query data print "query async..." items = DB\queryAsync "SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC" rows = [] for i = 1, #items item = items[i] rows[] = item[1] p rows Create a new thread to perform asynchronous operations. Asynchronous Insert: Insert global variable names into the test table. Asynchronous Query: Query values from the test table that don't start with "hello" and sort them in alphabetical order. Process Results: Extract the value from the query results and store it in the rows table. Print the results. 9. Summary In this tutorial, you have learned how to use Dora SSR engine's SQLite database functionality to perform basic data operations. With these interfaces, you can efficiently manage various data in your games, providing players with a richer gaming experience. Tips: Always handle potential errors when performing database operations. Using transactions ensures atomicity of data operations, avoiding data inconsistency. Next Steps: Learn more advanced SQL syntax such as JOINs and subqueries. For detailed information on SQLite SQL syntax, refer to the official SQLite documentation. Learn how to optimize database queries for better performance. Explore how to organize and manage database code in real-world projects.